import { AbstractAssetTask, Stage } from 'babylon-lib/index';
import { AbstractAssetTaskInfo, GLTFMeshTaskInfo } from 'babylon-lib/loader/asset-task-info';
import { AssetsLoad } from 'babylon-lib/loader/assets-load';
import { ILoose } from 'babylon-lib/tool/utils';
import { SkinnedClone } from 'babylon-lib/misc/skinned-clone';

enum AssetPostfix {
    glb = 'glb',
    jpg = 'jpg',
    mp3 = 'mp3'
}

/**
 * 资源管理器
 */
class AssetManager {

    /**
     * 资源容器
     */
    public static readonly assetMap: Map<string, AbstractAssetTaskInfo> = new Map();
    public static readonly assetInstaceMap: Map<string, SkinnedClone> = new Map();

    /**
     * 设置资源记录
     * @param key 资源路径作为key
     * @param assetTaskInfo 资源数据信息
     */
    public static setAsset(key: string, assetTaskInfo: AbstractAssetTaskInfo): void {
        this.assetMap.set(key, assetTaskInfo);
    }

    /**
     * 获取资源记录
     * @param key 记录的key值，一般为资源路径
     * @returns 资源数据信息
     */
    public static getAsset(key: string): AbstractAssetTaskInfo | null {
        return this.assetMap.get(key) || null;
    }
    public static getAssetInstace(key: string, stage: Stage): SkinnedClone | null {
        const instance = this.assetInstaceMap.get(key);
        if (instance) return instance;

        const asset = this.getAsset(key);
        if (!asset) return null;
        const ist = new SkinnedClone(key, stage);
        this.assetInstaceMap.set(key, ist);
        return ist;
    }

    /**
     * 加载多个资源，并记录（显示加载界面，一般用于初始化阶段）
     * @param keys 资源路径数组
     * @param loader 加载器
     * @param onStart 开始回调
     * @param onProgress 加载中回调
     * @param onTaskSuccess 单个资源完成时的回调
     * @param onFinish 所有资源加载完成时的回调
     * @param onTaskError 加载失败时的回调
     */
    public static load(keys: string[], loader: AssetsLoad,
        onStart?: { (): void },
        onProgress?: { (p: number): void },
        onTaskSuccess?: { (t: AbstractAssetTask): void },
        onFinish?: { (): void },
        onTaskError?: { (t: AbstractAssetTask): void }
    ): Promise<void> {
        if (keys.length === 0) return Promise.resolve();
        return new Promise((resolve, reject) => {
            const tasks: AbstractAssetTaskInfo[] = [];
            keys.forEach(key => {
                const task = this._generateTask(key);
                if (task) tasks.push(task);
            });
            loader.useLoadingScreen = true;
            loader.load(Object.assign({}, tasks) as unknown as ILoose<AbstractAssetTaskInfo>, onStart, onProgress, onTaskSuccess, () => {
                keys.forEach(key => {
                    const task = this._getTaskInfo(key, loader);
                    if (task) this.assetMap.set(key, task);
                });

                onFinish && onFinish();
                loader.scene.getEngine().hideLoadingUI();
                resolve();
            }, (t) => {
                onTaskError && onTaskError(t);
                reject();
            });
        });
    }

    /**
     * 获取单个资源，若资源已加载则直接返回，否则加载并记录（不显示加载界面，用于动态加载资源）
     * @param key 资源路径作为key值
     * @param loader 加载器
     * @param onStart 开始时的回调
     * @param onProgress 加载过程中的回调
     * @param onFinish 加载完成时的回调
     * @param onTaskError 加载失败时的回调
     * @returns 被加载的资源数据信息
     */
    public static async fetch(key: string, loader?: AssetsLoad,
        onStart?: { (): void },
        onFinish?: { (): void },
        onTaskError?: { (t: AbstractAssetTask): void }
    ): Promise<AbstractAssetTaskInfo | null> {

        if (this.assetMap.has(key)) return this.assetMap.get(key)!;
        if (!loader) return null;
        const task = this._generateTask(key);
        if (!task) return null;
        return new Promise<AbstractAssetTaskInfo | null>((resolve, reject) => {
            loader.useLoadingScreen = false;
            loader.loadAsync(task, onStart, () => {
                const tsk = this._getTaskInfo(key, loader)!;
                this.assetMap.set(key, tsk);
                resolve(tsk);
                onFinish && onFinish();
            }, (t) => {
                reject(null);
                onTaskError && onTaskError(t);
            });
        });
    }
    public static async fetchInstance(key: string, stage: Stage,
        onStart?: { (): void },
        onFinish?: { (): void },
        onTaskError?: { (t: AbstractAssetTask): void }
    ): Promise<SkinnedClone | null> {

        const ks = key.split('.');
        if (ks[ks.length - 1] !== AssetPostfix.glb) return null;

        if (this.assetInstaceMap.has(key)) return this.assetInstaceMap.get(key)!;
        const asset = await this.fetch(key, stage.loader(stage.mainScene), onStart, onFinish, onTaskError);
        if (!asset) return null;

        const ist = new SkinnedClone(key, stage);
        this.assetInstaceMap.set(key, ist);
        return ist;
    }

    /**
     * 根据后缀名生成对应的资源加载任务
     */
    private static _generateTask(key: string): AbstractAssetTaskInfo | null {
        const keys = key.split('.');
        let task: AbstractAssetTaskInfo | null = null;
        switch (keys[keys.length - 1]) {
            case AssetPostfix.glb:
                task = new GLTFMeshTaskInfo(key);
                break;
            case AssetPostfix.jpg:
                break;
            case AssetPostfix.mp3:
                break;
        }
        return task;
    }

    /**
     * 获取资源加载任务信息
     */
    private static _getTaskInfo(key: string, loader: AssetsLoad): AbstractAssetTaskInfo | null {
        const keys = key.split('.');
        switch (keys[keys.length - 1]) {
            case AssetPostfix.glb:
                return loader.getMeshTaskInfo(key);
            case AssetPostfix.jpg:
            case AssetPostfix.mp3:
        }
        return null;
    }

}


export { AssetManager };